package jude.java.framework.thread;

import java.util.ArrayList;
import java.util.List;

import jude.java.money.provide.Provider;
import jude.java.money.task.BaseTask;
import jude.java.util.I;
import jude.java.util.Utils;

public class Mission<T extends BaseTask<P>, P> extends ThreadGroup implements Stoppable, Controlable {
	private static final int MONITOR_INTERVAL = 15000;
	
	private static final int TIMEOUT = 180 * 1000;		//超时时间
	private static final int HALT = TIMEOUT * 2;		//挂机等待时间
	private static final int KILL = TIMEOUT * 6;		//kill时间
	
	private static int MISSION_INDEX = 1;
	private int THREAD_INDEX = 1;
	
	private TaskFactory<T, P> factory;
	private Provider<P> provider;
	
	private int threadCount;
	private Thread[] threads;
	private MonitorThread monitorThread;
	private List<WorkThread> blocked;
	private boolean stop;
	
	private boolean timer;
	private int times;

	public Mission(TaskFactory<T, P> factory, Provider<P> provider, int threadCount) {
		super("mission." + MISSION_INDEX++);
		this.factory = factory;
		this.provider = provider;
		this.threadCount = threadCount;
//		this.threads = new Thread[threadCount];
		this.timer = true;
	}
	
	public synchronized void start() {
		stop = false;
		if (threads == null) {
			threads = new Thread[threadCount];
			WorkThread thread = new WorkThread(getTask());
			thread.start();
			threads[0] = thread;
		}
		if (monitorThread == null || monitorThread.isAlive()) {
			monitorThread = new MonitorThread();
			monitorThread.start();
		}
	}
	
	public synchronized void pause() {
		stop = true;
	}
	
	public synchronized boolean toggleTimer() {
		timer = !timer;
		if (timer)
			times = 0;
		return timer;
	}
	
	public boolean isFinished() {
		boolean b = stop || provider.eof();
		return b && monitorThread.finished;
//		if (!b)
//			return false;
//		for (int i = 0; i < threads.length; i++) {
//			WorkThread thread = (WorkThread)threads[i];
//			if (thread != null && !thread.finished) {
//				return false;
//			}
//		}
//		return true;
	}
	
	private void monitor() {
		boolean finished = provider.eof();
		if (finished) {
			boolean all = true;
			for (int i = 0; i < threads.length; i++) {
				WorkThread thread = (WorkThread)threads[i];
				if (thread != null) {
					if (thread.finished == thread.isAlive()) {
						System.out.println(thread.finished + "/" + thread.isAlive() + "\t" + thread.getName());
					}
					if (!thread.finished) {
						all = false;
						break;
					}
				}
			}
			if (all) {
				monitorThread.finished = true;
				showBlocked();
				System.out.println(I.stat());
				System.out.println("=========== all task fininshed ==============");
				try {
					System.in.close();
					System.in.notifyAll();
				} catch (Exception e) {
				}
				return;
			}
		}
		for (int i = 0; i < threads.length; i++) {
			WorkThread thread = (WorkThread)threads[i];
			if (thread == null || !thread.isAlive()) {
				if (!finished) {
					thread = new WorkThread(getTask());
					thread.start();
					threads[i] = thread;
				}
				continue;
			}
			long timeout = thread.getTimeout();
			if (timeout > HALT) {
				threads[i] = null;
				thread.quit(true);
				if (!finished) {
					if (blocked == null) {
						blocked = new ArrayList<WorkThread>();
					}
					blocked.add(thread);
					thread = new WorkThread(getTask());
					thread.start();
					threads[i] = thread;
				}
			} else if (timeout > TIMEOUT) {
				thread.quit(false);
			}
		}
		if (blocked != null) {
			killBlocked(true);
			for (int i=blocked.size()-1; i>=0; i--) {
				WorkThread thread = blocked.get(i);
				if (!thread.isAlive()) {
					blocked.remove(thread);
					continue;
				}
			}
		}
	}
	
	private T getTask() {
		return factory.createTask(provider);
	}
	
	/**
	 * 统计信息
	 * */
	private String getStatistics() {
		StringBuilder sb = new StringBuilder(100);
		sb.append("(").append(Utils.toTime(MONITOR_INTERVAL / 1000 * times)).append(")");
		sb.append("\t").append(provider.getStatus());
		return sb.toString();
	}
	
	public void showRuntime() {
		if (isFinished()) {
			System.out.println("------ finished ------");
			return;
		}
		StringBuilder sb = new StringBuilder(300);
		for (int i = 0; i < threads.length; i++) {
			WorkThread thread = (WorkThread)threads[i];
			sb.append(i + 1).append(" ");
			if (thread == null || !thread.isAlive()) {
				sb.append("null");
			} else {
				sb.append(thread.getTaskDetails());
			}
			sb.append("\n");
		}
		System.out.println(sb.toString());
	}
	
	public void showBlocked() {
		if (blocked == null || blocked.isEmpty()) {
			System.out.println("------ empty ------");
			return;
		}
		StringBuilder sb = new StringBuilder(300);
		for (int i = 0, j = blocked.size(); i < j; i++) {
			WorkThread thread = blocked.get(i);
			sb.append(i + 1).append(" ");
			sb.append(thread.getTaskDetails()).append("\n");
		}
		System.out.println(sb.toString());
	}
	
	public void killBlocked() {
		killBlocked(false);
	}
	
	@SuppressWarnings("deprecation")
	private void killBlocked(boolean auto) {
		if (blocked == null || blocked.isEmpty()) {
			if (!auto)
				System.out.println("------ empty ------");
			return;
		}
		for (int i = 0, j = blocked.size(); i < j; i++) {
			WorkThread thread = blocked.get(i);
			if ((thread.isAlive() || !thread.finished) && thread.getTimeout() > KILL) {
				try {
					thread.quit(true);
					thread.interrupt();
					thread.stop();
				} catch (Throwable e) {
					System.out.println(e.getClass().getName());
				}
			}
		}
	}
	
	public void waitingAll() {
		try {
			monitorThread.join();
		} catch (InterruptedException e) {
		}
	}
	
	private class WorkThread extends Thread {
		private boolean finished;
		private BaseTask<?> task;

		public WorkThread(BaseTask<?> task) {
			super(Mission.this, Mission.this.getName() + "-work." + THREAD_INDEX++);
			this.task = task;
		}

		public void run() {
			if (task == null)
				return;
			while (!Mission.this.stop && !finished) {
				try {
					task.run();
					if (provider.eof()) {
						finished = true;
						break;
					}
				} catch (Exception t) {
					t.printStackTrace();
					//出错, 重新运行
				}
			}
		}
		
		public String getTaskDetails() {
			return task.getTaskDetails();
		}
		
		public long getTimeout() {
			return task.getTimeout();
		}
//		
//		public int getPosition() {
//			if (task instanceof Addressable) {
//				return ((Addressable)task).getPosition();
//			}
//			return 0;
//		}
		
		public void quit(boolean interrupt) {
			finished = true;
			if (task instanceof Stoppable) {
				((Stoppable)task).stop();
			}
			if (interrupt) {
				interrupt();
			}
		}
		
		@Override
		public int hashCode() {
			return getName().hashCode();
		}
		
		@Override
		public boolean equals(Object obj) {
			return this == obj;
//			if (obj == null || !obj.getClass().getName().equals(WorkThread.class.getName())) {
//				return false;
//			}
//			return getName().equals(((WorkThread)obj).getName());
		}
	}
	
	private class MonitorThread extends Thread {
		private boolean finished;
		
		public MonitorThread() {
			super(Mission.this, Mission.this.getName() + "-monitor");
		}
		
		@Override
		public void run() {
			while (!Mission.this.stop && !finished) {
				try {
					monitor();
				} catch (Exception e) {
					e.printStackTrace();
				}
				if (Mission.this.timer) {
					System.out.println(getStatistics());
					Mission.this.times ++;
				}
				Utils.sleep(MONITOR_INTERVAL);
			}
		}
	}


	@Override
	public String[] getCommands() {
		return new String[]{
				"help", "H", "start", "stop", "timer", "T", "Runtime", "R", "block", "B", "stat", "S", "kill"
		};
	}
	
	@Override
	public void onCommand(int index, String cmd) {
		if ("help".equals(cmd) || "H".equals(cmd)) {
			StringBuilder sb = new StringBuilder();
			sb.append("\tWelcome to ABC help page\n\n");
			sb.append("Help\tshow help\n");
//			sb.append("exit\tquit the command line\n");
			sb.append("start\tstart mission\n");
			sb.append("stop\tpause mission\n");
			sb.append("Timer\ttoggle timer\n");
			sb.append("Runtime\tshow current status\n");
			sb.append("Block\tshow blocked threads\n");
			sb.append("Stat\tstat infomations\n");
			sb.append("kill\tkill blocked threads\n");
			sb.append("\n");
			System.out.println(sb);
		} else if ("stop".equals(cmd)) {
			this.pause();
		} else if ("start".equals(cmd)) {
			this.start();
		} else if ("timer".equals(cmd) || "T".equals(cmd)) {
			this.toggleTimer();
		} else if ("Runtime".equals(cmd) || "R".equals(cmd)) {
			this.showRuntime();
		} else if ("block".equals(cmd) || "B".equals(cmd)) {
			this.showBlocked();
		} else if ("kill".equals(cmd)) {
			this.killBlocked();
		} else if ("stat".equals(cmd)) {
			System.out.println(I.stat());
		}
	}
	
}
